/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.jeff.springWebTestNG.concurrent;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This Emulator emulates the Producer-Consumer pattern, using BlockingQueue to simplify the implementation
 * 
 * @author zhou.zhefu
 */
public class ProducerConsumerEmulator {
    private BlockingQueue<Baozi> baoziStack = new LinkedBlockingQueue<Baozi>();
    
    private static final int N_EATER = 7;
    
    public void start() {
        Thread chef = new Thread(new Chef());
        List<Thread> eaters = new ArrayList<Thread>(N_EATER);
        for (int i = 1; i <= N_EATER; i++) {
            eaters.add(new Thread(new Eater(i)));
        }
        
        chef.start();
        for (Thread eater : eaters) {
            eater.start();
        }
        
        //join all the sub threads
        try {
            chef.join();
            for (Thread eater : eaters) {
                eater.join();
            }
            
            System.out.println("There's still " + baoziStack.size() + " Baozi on the stack. ");
            baoziStack.clear();
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            Logger.getLogger(ProducerConsumerEmulator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private class Baozi {
        int id;

        public Baozi(int id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "Baozi[" + id + "]";
        }
    }
    
    private class Eater implements Runnable {
        static final int N_MAX_PER_EATER = 10;
        int count = 0;
        int id;
        List<Baozi> stomach = new LinkedList<Baozi>();

        public Eater(int id) {
            this.id = id;
        }
        
        @Override
        public void run() {
            while (count < N_MAX_PER_EATER) {
                try {
                    Baozi b = baoziStack.take();
                    stomach.add(b);
                    System.out.println(b + " has been eaten by " + this);
                    
                    count ++;
                    Thread.sleep(50);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    Logger.getLogger(Eater.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            
            System.out.println(this + " was satisfied, by " + stomach);
        }

        @Override
        public String toString() {
            return "Eater[" + id + "]";
        }
    }
    
    private class Chef implements Runnable {
        static final int N_MAX_BAOZI = 100;
        int count = 0;
        
        @Override
        public void run() {
            while (count < N_MAX_BAOZI) {
                try {
                    Baozi b = new Baozi(count);
                    baoziStack.offer(b);
                    System.out.println(b + " was available now. ");
                    
                    count ++;
                    Thread.sleep(10);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    Logger.getLogger(Chef.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            
            System.out.println("Chef got his job DONE! ");
        }
        
    }
    
    
}
